/**
 * Implementation of server component.
 * Init and start server instance.
 */
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var fs = require('fs');
var path = require('path');
var pathUtil = require('../util/pathUtil');
var Loader = require('pomelo-loader');
var utils = require('../util/utils');
var schedule = require('pomelo-scheduler');
var events = require('../util/events');
var Constants = require('../util/constants');
var FilterService = require('../common/service/filterService');
var HandlerService = require('../common/service/handlerService');

var ST_INITED = 0;    // server inited  //服务器已经初始化
var ST_STARTED = 1;   // server started //服务器已经启动
var ST_STOPED = 2;    // server stoped  //服务器已经停止

/**
 * Server factory function.
 * Server工厂函数
 * @param {Object} app  current application context
 * @return {Object} erver instance
 */
module.exports.create = function(app, opts) {
  return new Server(app, opts);//创建一个新的实例
};

var Server = function (app, opts) {
  this.opts = opts || {};//选项
  this.app = app;//上下文
  this.globalFilterService = null;//全局过滤器服务
  this.filterService = null;//过滤器服务
  this.handlerService = null;//处理器服务
  //过滤和处理就像两个兄弟，时刻在一起，哈哈。其实这是pomelo的请求处理链所决定的
  this.crons = [];//定时任务数组
  this.jobs = {};//任务
  this.state = ST_INITED;//更改状态为已初始化

  app.event.on(events.ADD_CRONS, this.addCrons.bind(this));//监听 "添加定时任务" 事件
  app.event.on(events.REMOVE_CRONS, this.removeCrons.bind(this));//监听 "删除定时任务" 事件
};

var pro = Server.prototype;

/**
 * Server lifecycle callback  服务器生命周期start回调
 */
pro.start = function() {
  if(this.state > ST_INITED) {//状态判断
    return;
  }

  this.globalFilterService = initFilter(true, this.app);//初始化全局过滤器
  this.filterService = initFilter(false, this.app);//初始化一般过滤器
  this.handlerService = initHandler(this.app, this.opts);//初始化处理器
  this.cronHandlers = loadCronHandlers(this.app);//加载定时任务处理器
  loadCrons(this, this.app);//加载定时任务配置
  this.state = ST_STARTED;//更改状态为已启动
};
//服务器生命周期afterStart回调

pro.afterStart = function() {
  scheduleCrons(this, this.crons);//开启定时任务
};

/**
 * Stop server    服务器生命周期stop回调
 */
pro.stop = function() {
  this.state = ST_STOPED;
};

/**
 * Global handler.
 * 全局处理函数，该函数在connector.js中的handleMessage函数定义中被调用 所以应该是作用于前端服务器的
 * @param  {Object} msg request message //请求消息
 * @param  {Object} session session object  //会话对象
 * @param  {Callback} callback function     //回调函数
 */
pro.globalHandle = function(msg, session, cb) {
  if(this.state !== ST_STARTED) {//状态判断 server组件
    utils.invokeCallback(cb, new Error('server not started'));
    return;
  }

  var routeRecord = parseRoute(msg.route);//解析路由
  if(!routeRecord) {//如果返回的路由是空的
    utils.invokeCallback(cb, new Error('meet unknown route message %j', msg.route));//调用回调报错
    return;
  }

  var self = this;
  var dispatch = function(err, resp, opts) {//定义派发函数 在before过滤器处理完之后调用
    if(err) {//全局before过滤器处理时出现错误
      handleError(true, self, err, msg, session, resp, opts, function(err, resp, opts) {
        response(true, self, err, msg, session, resp, opts, cb);
      });
      return;
    }

    if(self.app.getServerType() !== routeRecord.serverType) {//如果请求路由中的服务器类型不是当前服务器类型
      doForward(self.app, msg, session, routeRecord, function(err, resp, opts) {//转发请求
        response(true, self, err, msg, session, resp, opts, cb);
      });
    } else {//是本服务器上的请求，处理请求
      doHandle(self, msg, session, routeRecord, function(err, resp, opts) {
        response(true, self, err, msg, session, resp, opts, cb);
      });
    }
  };
  beforeFilter(true, self, msg, session, dispatch);//先触发全局过滤器
};

/**
 * Handle request
 */
pro.handle = function(msg, session, cb) {
   if(this.state !== ST_STARTED) {
    cb(new Error('server not started'));
    return;
  }

  var routeRecord = parseRoute(msg.route);
  doHandle(this, msg, session, routeRecord, cb);
};

/**
 * Add crons at runtime.
 * //在运行时添加定时任务
 * @param {Array} crons would be added in application
 */
pro.addCrons = function(crons) {
  this.cronHandlers = loadCronHandlers(this.app);
  for(var i=0, l=crons.length; i<l; i++) {
    var cron = crons[i];
    checkAndAdd(cron, this.crons, this);
  }
  scheduleCrons(this, crons);
};

/**
 * Remove crons at runtime.
 * //在运行时删除定时任务
 * @param {Array} crons would be removed in application
 */
pro.removeCrons = function(crons) {
  for(var i=0, l=crons.length; i<l; i++) {
    var cron = crons[i];
    var id = parseInt(cron.id);
    if(!!this.jobs[id]) {
      schedule.cancelJob(this.jobs[id]);
    } else {
      logger.warn('cron is not in application: %j', cron);
    }
  }
};

var initFilter = function(isGlobal, app) {//初始化过滤器
  //无论是全局过滤器 还是一般过滤器 其实都是一个 FilterService
  var service = new FilterService();//新建一个过滤器服务 将app添加的过滤器放入其中

  var befores, afters;

  if(isGlobal) {//如果是全局过滤器
    //获取全局的过滤器
    //此处的过滤器app.set处其实是在app上面的一些接口，比如before,after等等
    befores = app.get(Constants.KEYWORDS.GLOBAL_BEFORE_FILTER);
    afters = app.get(Constants.KEYWORDS.GLOBAL_AFTER_FILTER);
  } else {//一般过滤器
    //获取一般的过滤器
    befores = app.get(Constants.KEYWORDS.BEFORE_FILTER);
    afters = app.get(Constants.KEYWORDS.AFTER_FILTER);
  }

  var i, l;
  if(befores) {
    for(i=0, l=befores.length; i<l; i++) {//遍历before过滤器
      service.before(befores[i]);//向service中添加before过滤器
    }
  }

  if(afters) {
    for(i=0, l=afters.length; i<l; i++) {
      service.after(afters[i]);//向service中添加before过滤器
    }
  }

  return service;//返回容器
};

var initHandler = function(app, opts) {
  return new HandlerService(app, opts);//创建处理器服务并返回
};

/**
 * Load cron handlers from current application
 * 从当前app中加载定时任务处理器
 */
var loadCronHandlers = function(app) {
  var p = pathUtil.getCronPath(app.getBase(), app.getServerType());
  if(p) {
    return Loader.load(p, app);//加载cron路径下的模块
  }
};

/**
 * Load crons from configure file
 * 从配置文件中加载定时任务
 */
var loadCrons = function(server, app) {
  var env = app.get(Constants.RESERVED.ENV);//获取当前运行环境
  var p = path.join(app.getBase(), Constants.FILEPATH.CRON);//配置文件路径
  if(!fs.existsSync(p)) {//如果配置文件不存在
    p = path.join(app.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.CRON));//新的路径写法 不同的运行环境放在不同的地方
    if (!fs.existsSync(p)) {//如果还不存在
      return;//返回
    }
  }
  app.loadConfigBaseApp(Constants.RESERVED.CRONS, Constants.FILEPATH.CRON);//加载定时配置文件 导入到app.setting
  var crons = app.get(Constants.RESERVED.CRONS);//获取刚加载的定时配置
  for(var serverType in crons) {//遍历所有定时任务配置，key是服务器类型
    if(app.serverType === serverType) {//如果当前app的服务器类型属于配置中的定时任务的服务器类型
      var list = crons[serverType];//取出任务列表(数组)
      for(var i = 0; i<list.length; i++) {//遍历所有定时任务
        if(!list[i].serverId) {//如果没有定义serverId 则定时任务将在所有同类服务器执行
          checkAndAdd(list[i], server.crons, server);//检查并添加定时任务
        } else {//在特定serverId的服务器下面执行
          if(app.serverId === list[i].serverId) {//当前服务器ID等于定时任务定义的服务器ID
            checkAndAdd(list[i], server.crons, server);//检查并添加定时任务
          }
        }
      }
    }
  }
};

/**
 * Fire before filter chain if any
 * 触发before filter处理链
 */
var beforeFilter = function(isGlobal, server, msg, session, cb) {
  var fm;
  if(isGlobal) {//如果是触发全局过滤器
    fm = server.globalFilterService;
  } else {//一般过滤器
    fm = server.filterService;
  }
  if(fm) {//如果存在过滤器的话
    fm.beforeFilter(msg, session, cb);//调用过滤器 调用完过滤器链以后 再调用cb 派发请求
  } else {
    utils.invokeCallback(cb);//调用cb 派发请求
  }
};

/**
 * Fire after filter chain if have
 * 触发 after filter处理链
 */
var afterFilter = function(isGlobal, server, err, msg, session, resp, opts, cb) {
  var fm;
  if(isGlobal) {
    fm = server.globalFilterService;
  } else {
    fm = server.filterService;
  }
  if(fm) {
    if(isGlobal) {
      fm.afterFilter(err, msg, session, resp, function() {
        // do nothing
      });
    } else {
      fm.afterFilter(err, msg, session, resp, function() {
        cb(err, resp, opts);
      });
    }
  }
};

/**
 * pass err to the global error handler if specified
 * 传递错误到全局的错误处理器（如果有指定的话）
 */
var handleError = function(isGlobal, server, err, msg, session, resp, opts, cb) {
  var handler;
  if(isGlobal) {//如果是全局错误处理
    handler = server.app.get(Constants.RESERVED.GLOBAL_ERROR_HANDLER);
  } else {//一般的错误处理
    handler = server.app.get(Constants.RESERVED.ERROR_HANDLER);
  }
  if(!handler) {//如果不存在错误处理
    logger.debug('no default error handler to resolve unknown exception. ' + err.stack);
    utils.invokeCallback(cb, err, resp, opts);
  } else {
    if(handler.length === 5) {//??
      handler(err, msg, resp, session, cb);
    } else {
       handler(err, msg, resp, session, opts, cb);     
    }
  }
};

/**
 * Send response to client and fire after filter chain if any.
 * 给客户端发送一个响应并触发after过滤链
 */

var response = function(isGlobal, server, err, msg, session, resp, opts, cb) {
  if(isGlobal) {//如果是全局的 全局的是在前端
    cb(err, resp, opts);
    // after filter should not interfere response
    //果然，在调用after过滤链的时候，响应已经发出去了
    afterFilter(isGlobal, server, err, msg, session, resp, opts, cb);
  } else {
    afterFilter(isGlobal, server, err, msg, session, resp, opts, cb);
  }
};

/**
 * Parse route string.
 * 解析路由字符串
 * @param  {String} route route string, such as: serverName.handlerName.methodName
 * @return {Object}       parse result object or null for illeagle route string
 */
var parseRoute = function(route) {//解析路由
  if(!route) {
    return null;
  }
  var ts = route.split('.');//使用字符'.'拆分字符串
  if(ts.length !== 3) {//不等于3是非法路由
    return null;
  }

  return {
    route: route,//完整路由
    serverType: ts[0],//服务器类型
    handler: ts[1],//处理模块文件名
    method: ts[2]//方法名
  };
};
//转发请求
var doForward = function(app, msg, session, routeRecord, cb) {
  var finished = false;
  //should route to other servers
  try {
    app.sysrpc[routeRecord.serverType].msgRemote.forwardMessage(
      session,
      msg,
      session.export(),
      function(err, resp, opts) {
        if(err) {
          logger.error('fail to process remote message:' + err.stack);
        }
        finished = true;
        utils.invokeCallback(cb, err, resp, opts);
      }
    );
  } catch(err) {
    if(!finished) {
      logger.error('fail to forward message:' + err.stack);
      utils.invokeCallback(cb, err);
    }
  }
};

var doHandle = function(server, msg, session, routeRecord, cb) {
  var originMsg = msg;
  msg = msg.body || {};
  msg.__route__ = originMsg.route;

  var self = server;

  var handle = function(err, resp, opts) {
    if(err) {
      // error from before filter
      handleError(false, self, err, msg, session, resp, opts, function(err, resp, opts) {
        response(false, self, err, msg, session, resp, opts, cb);
      });
      return;
    }

    self.handlerService.handle(routeRecord, msg, session, function(err, resp, opts) {
      if(err) {
        //error from handler
        handleError(false, self, err, msg, session, resp, opts, function(err, resp, opts) {
          response(false, self, err, msg, session, resp, opts, cb);
        });
        return;
      }

      response(false, self, err, msg, session, resp, opts, cb);
    });
  };  //end of handle

  beforeFilter(false, server, msg, session, handle);//处理请求先，先启动一般before过滤链
};

/**
 * Schedule crons
 * //启动定时任务
 */
var scheduleCrons = function(server, crons) {
  var handlers = server.cronHandlers;//取得所有定时任务处理器
  for(var i = 0; i<crons.length; i++) {//遍历所有定时任务
    var cronInfo = crons[i];//取得当前定时任务的全部信息
    var time = cronInfo.time;//时间
    var action = cronInfo.action;//动作
    var jobId = cronInfo.id;//任务ID

    if(!time || !action || !jobId) {//如果时间未定义或者动作未定义或者任务ID未定义
      logger.error('cron miss necessary parameters: %j', cronInfo);//出错 定时任务丢失了必要的参数
      continue;
    }

    if(action.indexOf('.') < 0) {//动作中如果不存在符号'.'
      logger.error('cron action is error format: %j', cronInfo);//出错 动作定义是错误的格式
      continue;
    }
    
    var cron = action.split('.')[0];//模块名
    var job = action.split('.')[1];//方法
    var handler = handlers[cron];//根据模块名取得对应的模块
    
    if(!handler) {//如果对应的模块不存在
      logger.error('could not find cron: %j', cronInfo);//出错 找不到定时任务模块
      continue;
    }
      
    if(typeof handler[job] !== 'function') {//如果模块内对应的方法不是函数
      logger.error('could not find cron job: %j, %s', cronInfo, job);//出错 找不到对应的模块方法
      continue;
    }
      
    var id = schedule.scheduleJob(time, handler[job].bind(handler));//使用pomelo-scheduler模块调度任务(到时执行任务)
    server.jobs[jobId] = id;//保存任务得到ID值
  }
};

/**
 * If cron is not in crons then put it in the array.
 * 如果定时任务不在crons内，则添加到crons数组里
 */
var checkAndAdd = function(cron, crons, server) {
  if(!containCron(cron.id, crons)) {
    server.crons.push(cron);//添加到crons数组内
  } else {
    logger.warn('cron is duplicated: %j', cron);//警告 cron重复
  }
};

/**
 * Check if cron is in crons.
 * //检查cron是否在crons数组内
 */
var containCron = function(id, crons) {
  for(var i=0, l=crons.length; i<l; i++) {//遍历定时任务数组
    if(id === crons[i].id) {//如果ID相等
      return true;//返回true
    }
  }
  return false;//否则返回false
};
